// --------------------------------------------------------------------
//
//  Copyright (c) Microsoft Corporation.  All rights reserved
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// --------------------------------------------------------------------

            ////////////////////////////////////
            //                                //
            // Transaction Sample Application //
            //                                //
            ////////////////////////////////////


#define UNICODE                     // For all MSMQ applications

#include <stdio.h>


//------------------------------------------------------------------------------
// Include MS DTC-specific header files.

//------------------------------------------------------------------------------
#define INITGUID
#include <transact.h>

// Because we are compiling in UNICODE, there is a problem with DTC.
//#include <xolehlp.h>
extern HRESULT DtcGetTransactionManager(
                                    LPSTR  pszHost,
                                    LPSTR   pszTmName,
                                    REFIID rid,
                                    DWORD   dwReserved1,
                                    WORD    wcbReserved2,
                                    void FAR * pvReserved2,
                                    void** ppvObject )  ;

//------------------------------------------------------------------------------
// Include the ODBC-specific header file.
//------------------------------------------------------------------------------
#ifndef DBNTWIN32
#define DBNTWIN32

#include <SQLEXT.h>

// from  <odbcss.h>
#define SQL_COPT_SS_BASE               1200
#define SQL_COPT_SS_ENLIST_IN_DTC      (SQL_COPT_SS_BASE+7) // Enlist in a Viper transaction

// Define constant for use with SQL_ENLIST_IN_DTC.
#define SQL_DTC_DONE 0L       // Delimits end of Viper transaction

#endif

//--------------------------------------------------------------------------
//  Enable Ansi ODBC on VC5.
//--------------------------------------------------------------------------

#ifdef  SQLExecDirect
#undef  SQLExecDirect
#define SQLExecDirect SQLExecDirectA
#endif

#ifdef  SQLSetConnectOption
#undef  SQLSetConnectOption
#define SQLSetConnectOption  SQLSetConnectOptionA
#endif

#ifdef  SQLError
#undef  SQLError
#define SQLError  SQLErrorA
#endif

#ifdef  SQLConnect
#undef  SQLConnect
#define SQLConnect  SQLConnectA
#endif

//------------------------------------------------------------------------------
// Include the MSMQ-specific header file.
//------------------------------------------------------------------------------
#include "mq.h"

//------------------------------------------------------------------------------
// Define constants
//------------------------------------------------------------------------------
#define STR_LEN      40
#define MAX_VAR      20
#define MAX_FORMAT  100


//------------------------------------------------------------------------------
// Define datatypes
//------------------------------------------------------------------------------
typedef struct DBCONN
{
   char  pszSrv [STR_LEN];    // Data source name, configured through control panel
   char  pszUser [STR_LEN];   // Logon user name
   char  pszPasswd[STR_LEN];  // Logon user password
   HDBC  hdbc;                // Handle to an ODBC database connection
   HSTMT hstmt;               // ODBC statement handle, for use with SQLExecDirect

}  DBCONN;


//------------------------------------------------------------------------------
// Define globals
//------------------------------------------------------------------------------

// Global DB connection structure for the server
static DBCONN  gSrv =
   {  "MSMQDemo",
      "sa",
      "",
      SQL_NULL_HDBC,
      SQL_NULL_HSTMT
   };


// Service type GUID type for MQTransTest queues
static CLSID guidMQTransTestType =
{ 0xb856ab1, 0x16b6, 0x11d0, { 0x80, 0x48, 0x0, 0xa0, 0x24, 0x53, 0xc1, 0x6f } };


// Handle to ODBC environment
HENV  g_hEnv = SQL_NULL_HENV ;


// Buffer for computer name
WCHAR g_wszMachineName[ MAX_COMPUTERNAME_LENGTH + 1 ];


//--------------------------------------------------------------------------
// Forward declaration of routines used.
//--------------------------------------------------------------------------

void LogonToDB(DBCONN *ptr);
void ExecuteStatement(DBCONN *ptr, char* pszBuf, BOOL ProcessFlag);
BOOL ProcessRetCode(char*   pszFuncName,
                    DBCONN  *ptr,
                    RETCODE retcode,
                    BOOL    fExit = TRUE);
void DoSQLError(DBCONN *ptr);
void FreeODBCHandles(DBCONN *ptr);
void Error(char *s, HRESULT hr);
void Syntax();
void LocateTargetQueue(CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT]);
void PrepareSendMessageProperties(MSGPROPID     amPropId[MAX_VAR],
                          MQPROPVARIANT aPropVar[MAX_VAR],
                          MQMSGPROPS    &msgprops,
                          DWORD        &TransferSum);
void CreateQueue(CLSID *pGuidType, WCHAR wsFormat[]);
void GetMachineName();
void DisplayDollars (DBCONN *ptr, char *psAccount);


//------------------------------------------------------------------------------
// Sender Mode:
//
// The sender side does the following:
//   1. Creates the "SenderAccount" database.
//   2. Locates an MSMQ queue of type MQTransTest and opens it.
//       (NOTE: For simplicity, this sample assumes that there is only one queue of this type.)
//   3. In a loop:
//            Prompts the user to enter TransferSum.
//            Creates a transaction using MS DTC.
//            Within the transaction:
//                 Updates the "SenderAccount" database (subtracts TransferSum).
//                 Sends a message to the receiver side.
//            Commits the transaction.
//
//    4. Cleans up.
//
//
//
// The transaction in Sender mode includes two operations:
// (1) Updating the "SenderAccount" database (subtracting TransferSum).
// (2) Sending a message to the receiver side.
//------------------------------------------------------------------------------

void Sender()
{

   ITransactionDispenser   *pTransactionDispenser;
   ITransaction            *pTransaction;
   BOOL                    fTransactionCommitFlag;
                            // Used to decide whether to commit or abort

   HRESULT              hr;
   RETCODE              retcode;
   DWORD                dwTransferSum;              // Set by the user
   char                 sUserString[ STR_LEN ];
   char                 sSQLStatement[ STR_LEN*2 ];

   MQMSGPROPS           msgprops;
   MQPROPVARIANT        aPropVar[MAX_VAR];
   MSGPROPID            amPropId[MAX_VAR];
   WCHAR                wsFormat[MAX_FORMAT];
   QUEUEHANDLE          aqh;


    printf("\nSender Side.\n\n");

   //---------------------------------------------------------------------
   // Build the "SenderAccount" database (with the sum of $1000)
   //---------------------------------------------------------------------

   printf ("Building SenderAccount with the sum of $1000...\n");

   // Get the ODBC environment handle.
   retcode = SQLAllocEnv(&g_hEnv);

   ProcessRetCode("SQLAllocEnv",0, retcode);

   // Establish a connection to the database.
   LogonToDB(&gSrv);

   // Clear the database from the previous run.
   ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",FALSE);

   // Create a new table in the database.
   ExecuteStatement(&gSrv,
    "CREATE TABLE SenderAccount (Rate INTEGER CONSTRAINT c1 CHECK (Rate>=0))",TRUE);

   // Insert new data into the database.
   ExecuteStatement(&gSrv,"INSERT INTO SenderAccount   VALUES(1000)",TRUE);

   printf ("OK.\n\n");

   //-----------------------------------------------------------------------
   // Locate the destination queue and open it for sending messages.
   //-----------------------------------------------------------------------

   printf ("Searching for the receiver queue...\n");

   // Locate the destination queue.
   LocateTargetQueue (&guidMQTransTestType, wsFormat);

   // Open the destination queue.
   hr = MQOpenQueue(wsFormat, MQ_SEND_ACCESS, 0, &aqh);

   if (FAILED(hr))
   {
      Error ("MQOpenQueue failed", hr);
   }

   //--------------------------------------------------------------------------
   // Get the transaction dispenser.
   //--------------------------------------------------------------------------

   // Obtain an interface pointer from the MS DTC proxy.
   hr = DtcGetTransactionManager(
               NULL,                        // pszHost
               NULL,                        // pszTmName
               IID_ITransactionDispenser,   // IID of interface
               0,                           // Reserved -- must be NULL
               0,                           // Reserved -- must be NULL
               0,                           // Reserved -- must be NULL
               (void **)&pTransactionDispenser  // Pointer to pointer to requested interface
                                 );

   if (FAILED(hr))
   {
      Error ("DTCGetTransactionManager failed", hr);
   }

   //--------------------------------------------------------------------
   // Sender's main loop
   //--------------------------------------------------------------------
   while (TRUE)
   {

		// Prompt user to enter a value for TransferSum.
		printf ("\n\nPlease enter the sum of dollars to transfer, or '0' to quit  ==> ");

		// Read the user input.
      
		if (fgets (sUserString, STR_LEN - 1, stdin) == NULL)
		{
			printf("\nInvalid input was entered. Exiting...\n");
			exit(1);
		}

		sUserString[STR_LEN - 1] = '\0';

		//
		// Remove the final new-line character introduced by fgets.
		//
		if(sUserString[strlen(sUserString) - 1] == '\n')
		{	  
			sUserString[strlen(sUserString) - 1] = '\0';
		}

      // Convert the user string to a DWORD
      dwTransferSum = atoi(sUserString);

      // Prepare message properties to sending a message.
      PrepareSendMessageProperties (amPropId,
                                    aPropVar,
                                    msgprops,
                                    dwTransferSum);

      //---------------------------------------------------------------------
      // Create a transaction (within the sender's main loop).
      //---------------------------------------------------------------------

      printf ("\nStarting a transaction...\n\n");

      // Initiate an MS DTC transaction.
      hr = pTransactionDispenser->BeginTransaction (
            0,                         // Must be null
            ISOLATIONLEVEL_ISOLATED,   // Isolation level
            ISOFLAG_RETAIN_DONTCARE,   // Isolation flags
            0,                         // Pointer to transaction options object
            &pTransaction);            // Pointer to pointer to transaction object

      if (FAILED(hr))
      {
         Error ("BeginTransaction failed",hr);
      }

      // The default is to commit the transaction.
      fTransactionCommitFlag = TRUE;

      //
      // SQL is a resource manager in the transaction.
      // It must be enlisted.
      //

      // Enlist the database in the transaction.
      retcode = SQLSetConnectOption (gSrv.hdbc,
                                     SQL_COPT_SS_ENLIST_IN_DTC,
                                     (UDWORD)pTransaction);

      if (retcode != SQL_SUCCESS)
      {
         ProcessRetCode("SQLSetConnection", &gSrv, retcode, FALSE);
         fTransactionCommitFlag = FALSE;
      }


      // Prepare an SQL statement for updating SenderAccount.
      sprintf_s (sSQLStatement, sizeof(sSQLStatement),
               "UPDATE SenderAccount  SET Rate = Rate - %lu", dwTransferSum) ;

      // Allocate a statement handle for use with SQLExecDirect.
      retcode = SQLAllocStmt(gSrv.hdbc, &gSrv.hstmt);

      if (retcode != SQL_SUCCESS)
      {
         ProcessRetCode("SQLAllocStmt", &gSrv, retcode, FALSE);
         fTransactionCommitFlag = FALSE;
      }

      // Update the database (subtract TransferSum from SenderAccount).
      retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS);

      if (retcode != SQL_SUCCESS)
      {
         ProcessRetCode("SQLExecDirect", &gSrv, retcode, FALSE);
         fTransactionCommitFlag = FALSE;
      }

      // Free the statement handle.
      retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP);

      gSrv.hstmt = SQL_NULL_HSTMT;

      //
      // MSMQ is another resource manager in the transaction.
      // Its enlistment is implicit.
      //

      // Within the transaction, send a message to the receiver side.
      hr = MQSendMessage(aqh,              // Handle to destination queue
                         &msgprops,        // pointer to MQMSGPROPS structure
                         pTransaction);    // pointer to Transaction Object


      if (FAILED(hr))
      {
         printf("\nMQSendMessage() failed. Error code: %lxh\n", (DWORD) hr);
         fTransactionCommitFlag = FALSE;
      }


      // Commit the transaction.
      if (fTransactionCommitFlag)
      {
         printf ("Committing the transaction...\n");

         hr = pTransaction->Commit(0, 0, 0);

         if (FAILED(hr))
            printf ("The transaction was aborted.\n\n");
         else
            printf ("The transaction was committed successfully.\n\n");

      }
      else
      {
         printf ("Aborting the transaction...\n");

         hr = pTransaction->Abort(0, 0, 0);

         if (FAILED(hr))
            Error("The transaction was aborted", hr);
         else
            printf ("The transaction was aborted.\n\n");
      }

      // Release the transaction.
      pTransaction->Release();

      // End enlistment of the database.
      retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);

      ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);

      // Display the sum of dollars in SenderAccount.
      DisplayDollars (&gSrv, "SenderAccount");

      // Quit the loop when nothing was transferred.
      if (dwTransferSum == 0)
         break;
   }

   //--------------------------------------------------------------------------
   // Cleanup
   //--------------------------------------------------------------------------

   // Release the transaction dispenser.
   pTransactionDispenser->Release();


   // Free the database.
   ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",TRUE);


   // Free the ODBC handle.
   FreeODBCHandles(&gSrv);


   // Free the ODBC environment handle.
   retcode = SQLFreeEnv(g_hEnv);

   if (retcode == SQL_ERROR)
      Error ("SQL FreeEnv ",0);


   // Free the MSMQ queue handle.
   MQCloseQueue(aqh);


   printf ("\n\nThe sender side completed.\n\n");

}




//------------------------------------------------------------------------------
// Receiver Mode:
//
// The receiver side does the following:
//    1. Creates the "ReceiverAccount" database.
//    2. Creates an MSMQ transactional public queue of type
//       MQTransTest locally and opens it.
//    3. In a loop:
//            Creates a transaction using MS DTC.
//            Within the transaction:
//                 Receives a message from the queue (containing the value of TransferSum).
//                 Updates the "ReceiverAccount" database (adds TransferSum).
//            Commits the transaction.
//    4. Cleans up.
//
//
//
// The transaction in the receiver mode includes two operations:
// (1) Receiving a message (sent by the sender side) from the queue.
// (2) Updating the "ReceiverAccount" database  (adding TransferSum).
//------------------------------------------------------------------------------

void Receiver()
{
   MSGPROPID            amPropId[MAX_VAR];
   MQMSGPROPS           msgprops;
   MQPROPVARIANT        aPropVar[MAX_VAR];
   DWORD             cProps;
   HRESULT              hr;
   WCHAR             wsFormat[MAX_FORMAT];
   QUEUEHANDLE          aqh;

   ITransactionDispenser   *pTransactionDispenser;
   ITransaction         *pTransaction;
   BOOL              TransactionCommitFlag;  // Used to decide whether to commit or abort

   RETCODE              retcode;
   DWORD             TransferSum;

   DWORD             MessageBuffer;       // Message body containing TransferSum
   char              sSQLStatement[STR_LEN*2];





   printf ("\nReceiver Side.\n\n");

   //-----------------------------------------------------------------------
   // Build a "ReceiverAccount" database (with the sum of $500).
   //-----------------------------------------------------------------------

   printf ("Building a ReceiverAccount with a sum of $500...\n");

   // Get ODBC environment handle.
   retcode = SQLAllocEnv(&g_hEnv);

   ProcessRetCode("SQLAllocEnv",0, retcode);

   // Establish a connection to the database.
   LogonToDB(&gSrv);

   // Clear the table from the previous run.
   ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",FALSE);

   // Create a new table.
   ExecuteStatement(&gSrv,"CREATE TABLE ReceiverAccount (Rate INTEGER CONSTRAINT c2 CHECK (Rate>0))",TRUE);

   // Insert new data into the table.
   ExecuteStatement(&gSrv,"INSERT INTO ReceiverAccount  VALUES(500)",TRUE);

   printf ("OK.\n\n");

   //-----------------------------------------------------------------------
   // Create queue and open it for receiving messages.
   //-----------------------------------------------------------------------

   printf ("Creating a receiver queue...\n");

   // Create the queue.
   CreateQueue (&guidMQTransTestType, wsFormat);

   // Prepare message properties for reading messages.
   cProps = 0;

   amPropId[cProps] =             PROPID_M_BODY;

   aPropVar[cProps].vt =          VT_UI1 | VT_VECTOR;
   aPropVar[cProps].caub.cElems =  sizeof(MessageBuffer);
   aPropVar[cProps].caub.pElems =  (unsigned char *)&MessageBuffer;
   cProps++;

   // Create a MSGPROPS structure.
   msgprops.cProp =    cProps;
   msgprops.aPropID =  amPropId;
   msgprops.aPropVar = aPropVar;
   msgprops.aStatus =  0;

   // Open the queue.
   hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh);

   //
   // Things are a little bit tricky. MQCreateQueue succeeded, but in the 
   // case of a public queue, this does not mean that MQOpenQueue
   // will succeed, because replication delays are possible. The queue is
   // registered in the DS, but it might take a replication interval
   // until the replica reaches the server that I am connected to.
   // To overcome this, open the queue in a loop.
   //
   if (hr == MQ_ERROR_QUEUE_NOT_FOUND)
   {
       int iCount = 0 ;
       while((hr == MQ_ERROR_QUEUE_NOT_FOUND) && (iCount < 120))
       {
          printf(".");

          // Wait a bit.
          iCount++ ;
          Sleep(500);

          // Retry.
          hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh);
       }
   }

   if (FAILED(hr))
   {
      Error ("MQOpenQueue failed", hr);
   }

   printf("OK.");


   //--------------------------------------------------------------------------
   // Get the transaction dispenser.
   //--------------------------------------------------------------------------

   // Obtain an interface pointer from the MS DTC proxy.
   hr = DtcGetTransactionManager(
         NULL, NULL, // pszHost, pszTmName
         IID_ITransactionDispenser,         // IID of the requested interface
         0,0,0,                             // Reserved -- must be NULL
         (void **)&pTransactionDispenser);  // Pointer to pointer to the requested interface


   if (FAILED(hr))
      Error ("DTCGetTransactionManager failed", hr);


   //--------------------------------------------------------------------------
   // Receiver Main Loop
   //--------------------------------------------------------------------------
   while (TRUE)
   {

      printf ("\n\nWaiting for a message to come...\n");

      // Peek outside the transaction to avoid database lock
      // for a long/infinite period.
      //
      //dwSize = sizeof(wsResponse);
      hr = MQReceiveMessage(
                    aqh,                     // Handle to queue
                    INFINITE,                // Time-out
                    MQ_ACTION_PEEK_CURRENT,  // Peek action
                    &msgprops,               // Message properties
                    NULL,                    // No OVERLAPPED structure
                    NULL,                    // No callback function
                    NULL,                    // No cursor
                    NULL                     // No transaction yet
                           );

      if (FAILED(hr))
         Error("MQReceiveMessage (PEEKING) failed", hr);



      //--------------------------------------------------------------------------
      // Create a transaction.
      //--------------------------------------------------------------------------
      printf ("\n\nStarting a transaction...\n\n");


      // Initiate an MS DTC transaction.
      hr = pTransactionDispenser->BeginTransaction (
            0,                               // Must be NULL
            ISOLATIONLEVEL_ISOLATED,         // Isolation level
            ISOFLAG_RETAIN_DONTCARE,         // Isolation flags
            0,                               // Pointer to transaction options object
            &pTransaction);                  // Pointer to pointer to transaction object

      if (FAILED(hr))
         Error ("BeginTransaction failed", hr);


      // The default is to commit the transaction.
      TransactionCommitFlag = TRUE;

      //
      // SQL is a resource manager in the transaction.
      // It must be enlisted.
      //

      // Enlist the database in the transaction.
      retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, (UDWORD)pTransaction);

      if (retcode != SQL_SUCCESS)
         TransactionCommitFlag = FALSE;



      // Receive a message from the queue
      //dwSize = sizeof(wsResponse);
      hr = MQReceiveMessage(
            aqh,                              // Handle to queue
            INFINITE,                         // Time-out interval
            MQ_ACTION_RECEIVE,                // Receive action
            &msgprops,                        // Message properties
            NULL,NULL,NULL,                   // No OVERLAPPED structure, callback function, or cursor
            pTransaction);                    // Pointer to transaction object

      if (FAILED(hr))
         TransactionCommitFlag = FALSE;


      // The message buffer holds TransferSum.
      TransferSum = (DWORD)MessageBuffer;


      // Prepare an SQL statement to update ReceiverAccount.
      sprintf_s(sSQLStatement, sizeof(sSQLStatement), "UPDATE ReceiverAccount   SET Rate = Rate + %i", TransferSum);


      // Allocate a statement handle for use with SQLExecDirect.
      retcode = SQLAllocStmt(gSrv.hdbc,&gSrv.hstmt);

      if (retcode != SQL_SUCCESS)
         TransactionCommitFlag = FALSE;


      // Update the database (add TransferSum to ReceiverAccount).
      retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS);

      if (retcode != SQL_SUCCESS)
         TransactionCommitFlag = FALSE;


      // Free the statement handle.
      retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP);

      gSrv.hstmt = SQL_NULL_HSTMT;



      // Commit the transaction.
      if (TransactionCommitFlag)
      {
         printf ("Committing the transaction...   ");

         hr = pTransaction->Commit(0, 0, 0);

         if (FAILED(hr))
            printf ("Failed... The transaction was aborted.\n\n");
         else
            printf ("The transaction was committed successfully.\n\n");

      }


      // Abort the transaction.
      else
      {
         printf ("Aborting the transaction...\n");

         hr = pTransaction->Abort(0, 0, 0);

         if (FAILED(hr))
            Error("The transaction was aborted", hr);
         else
            printf ("The transaction was aborted.\n\n");

      }



      // Release the transaction.
      pTransaction->Release();


      // End the enlistment of the database.
      retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);

      ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);


      // Display the number of dollars in ReceiverAccount.
      DisplayDollars (&gSrv, "ReceiverAccount");


      // Decide whether to continue the loop.
      if (TransferSum == 0)
         break;


   }


   //--------------------------------------------------------------------------
   // Cleanup
   //--------------------------------------------------------------------------

   // Release the transaction dispenser.
   pTransactionDispenser->Release();


   // Free the database.
   ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",TRUE);


   // Free the ODBC handle.
   FreeODBCHandles(&gSrv);


   // Free the ODBC environment handle.
   retcode = SQLFreeEnv(g_hEnv);

   if (retcode == SQL_ERROR)
      Error ("SQL FreeEnv ",0);


   // Free the queue handle.
    MQCloseQueue(aqh);


   // Delete the queue from the directory service.
   MQDeleteQueue(wsFormat);


   printf ("\n\nReceiver side completed.\n\n");
}

/*
//-----------------------------------------------------
// Check if teh local computer is enabled to access the 
// directory service (DS-enabled).
//----------------------------------------------------- 
bool DetectDsConnection(void)
{

    MQPRIVATEPROPS PrivateProps;
    QMPROPID       aPropId[MAX_VAR];
    MQPROPVARIANT  aPropVar[MAX_VAR];
    HRESULT        aStatus[MAX_VAR];
    DWORD          cProp;
    
    HRESULT        hr;

    //
    // Specify the PROPID_PC_DS_ENABLED property, which
	// indicates whether the local computer can access the DS.
	//
    cProp = 0;

    aPropId[cProp] = PROPID_PC_DS_ENABLED;
    aPropVar[cProp].vt = VT_NULL;
    ++cProp;	

    // Create a PRIVATEPROPS structure.
    PrivateProps.cProp = cProp;
	PrivateProps.aPropID = aPropId;
	PrivateProps.aPropVar = aPropVar;
    PrivateProps.aStatus = aStatus;

    //
    // Retrieve the information.
    //
	hr = MQGetPrivateComputerInformation(
				     NULL,
					 &PrivateProps);
	if(FAILED(hr))
        Error("A DS connection cannot be detected", hr);
	   
    return PrivateProps.aPropVar[0].boolVal;
}
*/
BOOL IsDsEnabledLocaly()
/*++

Routine Description:
    
      The routine checks whether the local computer operates in
      domain (DS-enabled) mode or in workgroup (DS-disabled) mode.

Arguments:
    
      None

Return Value:
    
      TRUE     -  domain (DS-enabled) mode.
      FALSE    -  workgroup (DS-disabled) mode.

--*/

{
       
    MQPRIVATEPROPS PrivateProps;
    QMPROPID       aPropId[MAX_VAR];
    MQPROPVARIANT  aPropVar[MAX_VAR];
    DWORD          cProp;  
    HRESULT        hr;
    //
    // Specify the PROPID_PC_DS_ENABLED property, which
	// indicates whether the local computer can access the DS.
    //
    cProp = 0;

    aPropId[cProp] = PROPID_PC_DS_ENABLED;
    aPropVar[cProp].vt = VT_NULL;
    ++cProp;	
    //
    // Create a PRIVATEPROPS structure.
    //
    PrivateProps.cProp = cProp;
	PrivateProps.aPropID = aPropId;
	PrivateProps.aPropVar = aPropVar;
    PrivateProps.aStatus = NULL;

    //
    // Retrieve the information.
    //
    

    //
    // This code is used to detect a DS connection.
    // This code is designed to allow compilation both on 
    // Windows NT 4.0 and on Windows 2000 and later.
    //
    HINSTANCE hMqrtLibrary = GetModuleHandle(TEXT("mqrt.dll"));
	if(hMqrtLibrary == NULL)
	{
		printf("An incomplete installation of MSMQ was detected. Exiting...\n");
		exit(1);
	}

    typedef HRESULT (APIENTRY *MQGetPrivateComputerInformation_ROUTINE)(LPCWSTR , MQPRIVATEPROPS*);
    MQGetPrivateComputerInformation_ROUTINE pfMQGetPrivateComputerInformation = 
          (MQGetPrivateComputerInformation_ROUTINE)GetProcAddress(hMqrtLibrary,
													 "MQGetPrivateComputerInformation");
    if(pfMQGetPrivateComputerInformation == NULL)
    {
        //
        // There is no entry point in the DLL that matches this routine.
        // It must be an old version of mqrt.dll (MSMQ 1.0).
        // It will be OK to handle this case as the case of DS-enabled computer.
        //
        return TRUE;
    }

	hr = pfMQGetPrivateComputerInformation(
				     NULL,
					 &PrivateProps);
	if(FAILED(hr))
	{
        //
        // We were not able to determine whether the computer is DS-enabled or DS-disabled.
        // Notify the user and assume the worst case (i.e., the computer is DS-disasbled).
        //
        Error("No DS connection could be detected", hr);
        return FALSE;
    }                             
	
    
    if(PrivateProps.aPropVar[0].boolVal == 0)
    {
        //
        // DS-disabled.
        //
        return FALSE;
    }

    return TRUE;

}



//------------------------------------------------------------------------------
//  MAIN
//------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   DWORD dwSize;

    if(argc != 2)
        Syntax();

    // Fail if the local computer is DS-disabled.
    if(!IsDsEnabledLocaly())
    {
        printf("We cannot work on a DS-disabled computer.\nExiting...");
        exit(1);
    }


    // Retrieve computer name.
    dwSize = MAX_COMPUTERNAME_LENGTH;
	if(!GetComputerName(g_wszMachineName, &dwSize))
	{
		printf("The computer name cannot be retrieved.\nExiting...");
        exit(1);
	}

    if(strcmp(argv[1], "-s") == 0)
        Sender();

    else if(strcmp(argv[1], "-r") == 0)
        Receiver();

    else
        Syntax();

    return(1);

}



//------------------------------------------------------------------------------
// Subroutines
//------------------------------------------------------------------------------

void Error(char *s, HRESULT hr)
{

    printf("\n\n%s (0x%X). Exiting...\n", s, hr);
    exit(1);
}

//------------------------------------------------------------------------------

void Syntax()
{
    printf("\n");
    printf("Syntax: msmqtrans -s | -r\n");
    printf("\t-s - Sender Side\n");
    printf("\t-r - Receiver Side\n");
    exit(1);

}

//------------------------------------------------------------------------------

void LocateTargetQueue (CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT])
{

   DWORD      dwSize;
   DWORD      i;

   DWORD      cQueue;
   DWORD      cProps;
   HRESULT       hr;
   MQPROPERTYRESTRICTION   aPropRestriction[MAX_VAR];
   MQRESTRICTION        Restriction;
   MQCOLUMNSET          Column;
   QUEUEPROPID          aqPropId[MAX_VAR];
   HANDLE               hEnum;
   MQPROPVARIANT        aPropVar[MAX_VAR];

    //
    // Prepare parameters for locating queues.
    //
    // 
    // 1. Restriction: Queues with PROPID_Q_TYPE = MSMQTransTest service type GUIID
    //
    cProps = 0;

    aPropRestriction[cProps].rel = PREQ;
    aPropRestriction[cProps].prop = PROPID_Q_TYPE;
    aPropRestriction[cProps].prval.vt = VT_CLSID;
    aPropRestriction[cProps].prval.puuid = pGuidType;
    cProps++;

    Restriction.cRes = cProps;
    Restriction.paPropRes = aPropRestriction;

    //
    // 2. Columnset (the queue properties to retrieve) = queue GUID
    //   
    cProps = 0;
    aqPropId[cProps] = PROPID_Q_INSTANCE;
    cProps++;

    Column.cCol = cProps;
    Column.aCol = aqPropId;

    //
    // Issue the query to locate the queues.
    //
    hr = MQLocateBegin(NULL, &Restriction, &Column, NULL, &hEnum);

   if (FAILED(hr))
      Error ("MQLocateBegin failed", hr);


    //
    // Get the results.
    //
    cQueue = MAX_VAR;
    hr = MQLocateNext(hEnum, &cQueue, aPropVar);

   if (FAILED(hr))
      Error ("MQLocateNext failed", hr);

    hr = MQLocateEnd(hEnum);

    if(cQueue == 0)
    {
        // No queue could be found, so exit
        printf("No queue was found. Exiting...\n\n");
        exit(0);
    }


    printf("FOUND %d.\n", cQueue);

    dwSize = sizeof(WCHAR)*MAX_FORMAT;

   // Transform the queue GUID into a format name.
   hr = MQInstanceToFormatName(aPropVar[0].puuid, wsFormat, &dwSize);

   if (FAILED(hr))
      Error ("MQInstancetoFormatName failed", hr);


   // Free the memory that was allocated for the queue GUID during the query.
    for(i = 0; i < cQueue; i++)
      MQFreeMemory(aPropVar[i].puuid);


}


//------------------------------------------------------------------------------

void PrepareSendMessageProperties (MSGPROPID     amPropId[MAX_VAR],
                           MQPROPVARIANT aPropVar[MAX_VAR],
                           MQMSGPROPS    &msgprops,
                           DWORD     &TransferSum)
{

   DWORD      cProps;

    cProps = 0;
    amPropId[cProps] =             PROPID_M_BODY;
    aPropVar[cProps].vt =          VT_UI1 | VT_VECTOR;
    aPropVar[cProps].caub.cElems =  sizeof(TransferSum);
    aPropVar[cProps].caub.pElems =  (unsigned char *)&TransferSum;
    cProps++;

    // Create a MSGPROPS structure.
    msgprops.cProp =    cProps;
    msgprops.aPropID =  amPropId;
    msgprops.aPropVar = aPropVar;
    msgprops.aStatus =  0;

}

//--------------------------------------------------------------------------

void  CreateQueue (CLSID *pGuidType, WCHAR wsFormat[])
{
   QUEUEPROPID       aqPropId[MAX_VAR];
   WCHAR             wsPathName[1000];  // Long path name
   MQPROPVARIANT     aPropVar[MAX_VAR];
   DWORD             cProps;
   MQQUEUEPROPS      qprops;
   DWORD             dwSize;
   HRESULT           hr;

   //---------------------------------------------------------------------
   // Prepare properties to create a queue on the local computer.
   //---------------------------------------------------------------------
   cProps = 0;

   // Set the path name property.
   aqPropId[cProps] =          PROPID_Q_PATHNAME;

   swprintf_s(wsPathName, sizeof(wsPathName)/sizeof(wsPathName[0]), TEXT("%s\\MSMQDemo"), g_wszMachineName);
   aPropVar[cProps].vt =       VT_LPWSTR;
   aPropVar[cProps].pwszVal =  wsPathName;
   cProps++;

   // Set the transactional level of the queue to transactional.
   aqPropId[cProps] =        PROPID_Q_TRANSACTION;

   aPropVar[cProps].vt =     VT_UI1;
   aPropVar[cProps].bVal =   MQ_TRANSACTIONAL;
   cProps++;

   // Set the service type GUID of the queue (used to locate queues in a query).
   aqPropId[cProps] =        PROPID_Q_TYPE;

   aPropVar[cProps].vt =     VT_CLSID;
   aPropVar[cProps].puuid =  pGuidType;
   cProps++;

   // Create a QUEUEPROPS structure.
   qprops.cProp =    cProps;
   qprops.aPropID =  aqPropId;
   qprops.aPropVar = aPropVar;
   qprops.aStatus =  0;

   //-----------------------------------------------------------------------
   // Create the queue.
   //-----------------------------------------------------------------------
   dwSize = sizeof(WCHAR)*MAX_FORMAT;
   hr = MQCreateQueue(NULL, &qprops, wsFormat, &dwSize);

   if(FAILED(hr))
   {
      // The API failed, but not because the queue exists.
      if(hr != MQ_ERROR_QUEUE_EXISTS)
            Error("MQCreateQueue failed", hr);

      // The queue exists, so get its format name.
      // Note: Because the queue already exists, this sample assumes
      // that it was created earlier by this program, so we do not 
      // check whether the queue is transactional. If at this point the
      // queue is not transactional, the transactions will abort later.
      //
      hr = MQPathNameToFormatName(wsPathName, wsFormat, &dwSize);

      if (FAILED(hr))
         Error ("The format name cannot be retrieved", hr);
   }
}

//-------------------------------------------------------------------------------

void LogonToDB(DBCONN *ptr)
{
   RETCODE retcode = 0;

   retcode = SQLAllocConnect(g_hEnv, &(ptr->hdbc) );

   if (ProcessRetCode("SQLAllocConnect",ptr,retcode))
   {
      retcode = SQLConnect(ptr->hdbc,
                  (UCHAR *)(ptr->pszSrv),
                  SQL_NTS,
                  (UCHAR *)(ptr->pszUser),
                  SQL_NTS,
                  (UCHAR *)(ptr->pszPasswd),
                  SQL_NTS
                  );

      ProcessRetCode("SQLConnect",ptr,retcode);
   }
}

//------------------------------------------------------------------------------

void ExecuteStatement(DBCONN *ptr, char* pszBuf,BOOL ProcessFlag)
{
   RETCODE retcode = 0;

   // Allocate a statement handle for use with SQLExecDirect.
   retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt));

   if (ProcessFlag)
      ProcessRetCode("SQLAllocStmt",ptr,retcode);

   // Execute the string passed as a SQL statement.
    retcode = SQLExecDirect (ptr->hstmt,(UCHAR *) pszBuf,SQL_NTS);

   if (ProcessFlag)
      ProcessRetCode("SQLExecDirect",ptr,retcode);

   // Free the statement handle.
   retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP);
   ptr->hstmt = SQL_NULL_HSTMT;

   if (ProcessFlag)
      ProcessRetCode("SQLFreeStmt",ptr,retcode);

}

// ---------------------------------------------------------------------------

void DisplayDollars (DBCONN *ptr, char *psAccount)
{

   DWORD             DollarsSum;                  // IN SQL database
   SDWORD               cbValue;                  // OUT argument for SQL query
   char              sSQLStatement[STR_LEN*2];
   RETCODE              retcode;




   // Allocate a statement handle for use with SQLExecDirect.
   retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt));

   ProcessRetCode("SQLAllocStmt",ptr,retcode);


   // Prepare SQL statement for issuing a query.
   int n = _snprintf_s(sSQLStatement, sizeof(sSQLStatement), STR_LEN*2 -1, "SELECT * FROM %s", psAccount);
   if(n < 0)
   {
		printf("The string is too long for the buffer specified. Exiting...\n");
		exit(1);
   }	
   sSQLStatement[n] = L'\0';



   // Issue the SQL query.
   retcode = SQLExecDirect (ptr->hstmt,(UCHAR *)sSQLStatement,SQL_NTS);

   ProcessRetCode ("SQLExecDirect",ptr,retcode);


   // Prepare a data structure for retrieving the query results.
   retcode = SQLBindCol(ptr->hstmt,1,SQL_C_ULONG,&DollarsSum,0,(SQLLEN *)&cbValue);

   ProcessRetCode ("SQLBindCol",ptr,retcode);


   // Retrieve the query results.
   retcode = SQLFetch (ptr->hstmt);

   ProcessRetCode ("SQLFetch",ptr,retcode);


   // Display the query results.
   printf ("Sum of dollars in %s is %d .\n\n",psAccount,DollarsSum);


   // Free the statement handle.
   retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP);
   ptr->hstmt = SQL_NULL_HSTMT;

   ProcessRetCode("SQLFreeStmt",ptr,retcode);

}

// ---------------------------------------------------------------------------

void FreeODBCHandles(DBCONN *ptr)
{
   SQLDisconnect(ptr->hdbc);

   SQLFreeConnect(ptr->hdbc);

   ptr->hdbc   = SQL_NULL_HDBC;
   ptr->hstmt  = SQL_NULL_HSTMT;
}


// ---------------------------------------------------------------------------

BOOL ProcessRetCode(char*   pszFuncName,
                    DBCONN  *ptr,
                    RETCODE retcode,
                    BOOL    fExit)
{
   BOOL state = TRUE ;
   BOOL fExitP = fExit ;

   switch (retcode)
   {

   case SQL_SUCCESS:
         fExitP = FALSE ;
         break;

   case SQL_SUCCESS_WITH_INFO:
         fExitP = FALSE ;
         break;

   case SQL_ERROR:
         printf("%s failed - see more info\n",pszFuncName);
         DoSQLError(ptr);
         state = FALSE;
         break;

   case SQL_INVALID_HANDLE:
         printf("%s failed - SQL_INVALID_HANDLE\n",pszFuncName);
         state = FALSE;
         break;

   case SQL_NO_DATA_FOUND:
         printf("%s failed - SQL_NO_DATA_FOUND\n",pszFuncName);
         fExitP = FALSE ;
         state = FALSE;
         break;

   case SQL_STILL_EXECUTING:
         printf("%s failed - SQL_STILL_EXECUTING\n",pszFuncName);
         fExitP = FALSE ;
         state = FALSE;
         break;

   case SQL_NEED_DATA:
         printf("%s failed - SQL_NEED_DATA\n",pszFuncName);
         fExitP = FALSE ;
         state = FALSE;
         break;

   default:
         printf("%s failed - unexpected error, retcode = %x\n",pszFuncName,retcode);
         DoSQLError(ptr);
         state = FALSE;
         break;
   }

   if (fExitP)
   {
      exit(-1) ;
   }
   return state ;
}

// ---------------------------------------------------------------------------

void DoSQLError(DBCONN *ptr)
{
   UCHAR                szSqlState[6];
   UCHAR             szErrorMsg[SQL_MAX_MESSAGE_LENGTH];

   SQLINTEGER  fNativeError   = 0;
   SWORD    cbErrorMsg = 0;
   RETCODE     retcode;

   retcode = SQLError(g_hEnv,
              ptr ? ptr->hdbc : 0,
              ptr ? ptr->hstmt :0,
              szSqlState,
              &fNativeError,
              szErrorMsg,
              sizeof(szErrorMsg),
              &cbErrorMsg
              );

   if (retcode != SQL_NO_DATA_FOUND && retcode != SQL_ERROR)
   {
      if (fNativeError != 0x1645)   // ignore change database to master context message
      {
         printf("SQLError info:\n");
         printf("SqlState: %s, fNativeError: %x\n",szSqlState,fNativeError);
         printf("Error Message: %s\n\n",szErrorMsg);
      }
   }
   else
   {
      printf("SQLError() failed: %x, NO_DATA_FOUND OR SQL_ERROR\n",retcode);
   }

}
// ---------------------------------------------------------------------------

